/********************************************************
Project name: OOP - Home Assignment no. 3

Authors: Rami Berkovsky
         Ofir Cohen

File name: Command.h

Purpose: Declaration of the command abstract base class, and its childs base classes:
         OneRegOperandCmd, TwoRegOperandCmd, CompositeCommand


********************************************************/

#ifndef _COMMAND_H_
#define _COMMAND_H_

#include <iostream>
#include "Risc.h"
#include "Exceptions.h"

// Command names
#define TWOREGCMD "TwoRegOperandCmd"
#define ONEREGCMD "OneRegOperandCmd"
#define IFCMD "if"
#define LOOPCMD "loop"
#define REGNAME "reg"


class Command {
public:
    // Constructors
    Command() {};                // Default constructor
    virtual ~Command() {};       // Destructor that implements polymorphism
    Command(const Command &) {}; // Copy constructor
    
    // Methods
    virtual void Execute() = 0;                                          // Executes the concrete command
	virtual Command* clone() const = 0;	                                 // Creates a copy of the object at run-time
	virtual void set(const int &dstReg, const int &srcReg) {};	         // Setter for cmd's with two-reg operands
	virtual void set(const int &dstReg, const unsigned long &srcVal) {}; // Setter for cmd's with one-reg operand
	virtual void set(const int &dstReg) {};	                             // Setter for loop cmd
	virtual string getType() const = 0;                                  // Returns concrete object name at run-time
};


// Abstract base class for the following 1-reg commands:
// load, addi, subi

class OneRegOperandCmd:public Command {
public:
    OneRegOperandCmd(const int &reg1=-1, const unsigned long &val=0): _dstReg(reg1), _srcVal(val) {};
	virtual Command* clone() const = 0;	// Clone object
	virtual void set(const int &dstReg, const unsigned long &srcVal) {
		_srcVal = srcVal;
		_dstReg = dstReg;
	};
	virtual string getType() const { return ONEREGCMD; };

protected:
    int _dstReg;
    unsigned long _srcVal;
};

// Abstract base class for the following 2-operand method:
// and, or, xor, sub, add, move

class TwoRegOperandCmd:public Command {
public:
    TwoRegOperandCmd(const int &reg1=-1, const int &reg2=-1): _dstReg(reg1), _srcReg (reg2) {};

    virtual void Execute() = 0;  // Virtual unimplemented function
	virtual string getType() const { return TWOREGCMD; };
	virtual void set(const int &dstReg, const int &srcReg) {
		_srcReg = srcReg;
		_dstReg = dstReg;
	};

protected:
    int _dstReg;      // destination reg (index)
    int _srcReg;      // source reg (index)
};

// Abstract base class for:
// LOOP, IF

class CompositeCommand:public Command {
protected:
	vector<Command*> myVec;
public:
	virtual void Execute() = 0;
	void add(Command* cmd) {
		myVec.push_back(cmd);
	};
};


// command: 'and reg1,reg2'
class AndCmd:public TwoRegOperandCmd {
public:
    void Execute() {
		unsigned long srcval = Risc::getInstance()[_srcReg];
		Risc::getInstance()[_dstReg] = (Risc::getInstance()[_dstReg]) & (srcval);
    };
	virtual Command* clone() const {
		Command* cmd = new AndCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'or reg1,reg2'
class OrCmd:public TwoRegOperandCmd {
public:
    void Execute() {
		unsigned long srcval = Risc::getInstance()[_srcReg];
		Risc::getInstance()[_dstReg] = (Risc::getInstance()[_dstReg]) | (srcval);
    };
	virtual Command* clone() const { 
		Command* cmd = new OrCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'xor reg1,reg2'
class XorCmd:public TwoRegOperandCmd {
public:
    void Execute() {
		unsigned long srcval = Risc::getInstance()[_srcReg];
		Risc::getInstance()[_dstReg] = (Risc::getInstance()[_dstReg]) ^ (srcval);
    };
	virtual Command* clone() const { 
		Command* cmd = new XorCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'add reg1,reg2'
class AddCmd:public TwoRegOperandCmd {
public:
    void Execute() {
		unsigned long srcval = Risc::getInstance()[_srcReg];
		Risc::getInstance()[_dstReg] = (Risc::getInstance()[_dstReg]) + (srcval);
    };
	virtual Command* clone() const { 
		Command* cmd = new AddCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'sub reg1,reg2'
class SubCmd:public TwoRegOperandCmd {
public:
    void Execute() {
		unsigned long srcval = Risc::getInstance()[_srcReg];
		Risc::getInstance()[_dstReg] = (Risc::getInstance()[_dstReg]) - (srcval);
    };
	virtual Command* clone() const { 
		Command* cmd = new SubCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};


// command: 'move reg1,reg2'
class MoveCmd:public TwoRegOperandCmd {
public:
    void Execute() {
		unsigned long srcval = Risc::getInstance()[_srcReg];
		Risc::getInstance()[_dstReg] = (srcval);
    };
	virtual Command* clone() const { 
		Command* cmd = new MoveCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'load reg,val'
class LoadCmd:public OneRegOperandCmd {
public:
    void Execute() {
        Risc::getInstance()[_dstReg] = _srcVal;
    };
	virtual Command* clone() const { 
		Command* cmd = new LoadCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'subi reg,val'
class SubiCmd:public OneRegOperandCmd {
public:
    void Execute() {
        Risc::getInstance()[_dstReg] -= _srcVal;
    };
	virtual Command* clone() const { 
		Command* cmd = new SubiCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};

// command: 'addi reg,val'
class AddiCmd:public OneRegOperandCmd {
public:
    void Execute() {
        Risc::getInstance()[_dstReg] += _srcVal;
    };
	virtual Command* clone() const { 
		Command* cmd = new AddiCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
};


// command: 'loop reg'
class LoopCmd:public CompositeCommand {
private:
	int _regcounter;
public:
	LoopCmd(const int &dstReg=-1): _regcounter(dstReg) {};
    virtual void Execute();
	virtual void set(const int &dstReg) {
        _regcounter = dstReg;
    };
	virtual Command* clone() const {
		Command* cmd = new LoopCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
	virtual string getType() const {
		return LOOPCMD;
	};
};

// command: 'if reg1,reg2'
class IfCmd:public CompositeCommand {
private:
	int _reg1;
	int _reg2;
	vector<Command*> myVecElse;
public:
	virtual void Execute();
	virtual void set(const int &dstReg, const int &srcReg) {
		_reg1 = dstReg;
		_reg2 = srcReg;
	};
	IfCmd(const int &srcReg=-1,const int &dstReg=-1): _reg1(srcReg), _reg2(dstReg) {};
	void addElse(Command* cmd) {
		myVecElse.push_back(cmd);
	};
	virtual Command* clone() const {
		Command* cmd = new IfCmd();
		if (cmd == NULL)
			throw MemoryAllocFailed();
		return cmd;
	};
	virtual string getType() const {
		return IFCMD;
	};
};

// IfCmd::execute implementation
inline void IfCmd::Execute() {
	if (Risc::getInstance()[_reg1] == Risc::getInstance()[_reg2])
	{
		vector<Command*>::iterator myIter;
		for (myIter = myVec.begin();myIter != myVec.end();myIter++)
		{
			(*myIter)->Execute();
		}
	} else {
		vector<Command*>::iterator myIter;
		for (myIter = myVecElse.begin();myIter != myVecElse.end();myIter++)
		{
			(*myIter)->Execute();
		}
	}
};

// LoopCmd::execute implementation
inline void LoopCmd::Execute() {
	int num = Risc::getInstance()[_regcounter];
	for (int i=0;i<num;i++)
	{
		vector<Command*>::iterator myIter;
		for (myIter = myVec.begin();myIter != myVec.end();myIter++)
		{
			(*myIter)->Execute();
		}
	}
};


#endif